package com.syl.framework.common.limit;

import com.syl.framework.common.QueryCondition;
import com.syl.framework.common.enums.DbType;
import com.syl.framework.common.limit.impl.MySqlCompute;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.log4j.Logger;

import java.sql.Connection;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Properties;

/**
 * 分页页数计算计算插件
 *
 * @author syl
 * @create 2018-05-01 11:16
 **/
//拦截StatementHandler的query方法 挑选相应的计算类
@Intercepts({
    @Signature(type = StatementHandler.class,
    method = "query",
    args = { Statement.class, ResultHandler.class }) })
public class PagingComputePlugin implements Interceptor {
    private static final Logger LOG = Logger.getLogger(PagingComputePlugin.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Statement statement = (Statement) invocation.getArgs()[0];
        String driverName = statement.getConnection().getMetaData().getDriverName();
        LOG.info("db type: "+driverName);

        StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
        //通过MetaObject优雅访问对象的属性，这里是访问statementHandler的属性
        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
        //先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler，然后就到BaseStatementHandler的成员变量mappedStatement
        StatementHandler delegate = (StatementHandler)metaObject.getValue("delegate");
        MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");

        // 配置文件中SQL语句的ID
        String id = mappedStatement.getId();
        //System.out.println("mapper sql id: "+id);
        Object object = delegate.getParameterHandler().getParameterObject();
        this.pagingHandler(object,driverName);//开始分页参数 处理

        BoundSql sql = delegate.getBoundSql();//获取到原本的sql
        //System.out.println(sql.getSql());
        //metaObject.setValue("delegate.boundSql.sql",newSql);//重写SQL语句
        return invocation.proceed();
    }

    /**
     * 参数处理类
     * @param object 未经处理的ParameterObject
     * @param driverName driverName
     */
    private void pagingHandler(Object object,String driverName){
        if(object == null)return;
        if(!(object instanceof HashMap))return;
        if(!((HashMap) object).containsKey("condition"))return;//确定是否是condition参数
        Object conditionObject = ((HashMap) object).get("condition");
        if(!(conditionObject instanceof QueryCondition))return;//确定类型是否一致
        QueryCondition condition = (QueryCondition) conditionObject;
        LOG.info("query condition :"+condition);
        if(condition.getPageCur() == null || condition.getPageRow() ==null)return;//
        pagingCompute(condition,getDbOption(driverName));
    }

    private DbType getDbOption(String driverName){
        driverName = driverName.toUpperCase().replace(" ","");//格式处理
        DbType dbType = null;
        if(driverName.indexOf("MYSQL") > -1){
            dbType = DbType.MYSQL;
        }else if (driverName.indexOf("SQLSERVER") > -1) {
            dbType = DbType.SQL_SERVER;
        }else if(driverName.indexOf("ORACLE") > -1){
            dbType = DbType.ORACLE;
        }else if(driverName.indexOf("POSTGRESQL") > -1){
            dbType = DbType.POSTGRE_SQL;
        }else if(driverName.indexOf("SQLITE") > -1){
            dbType = DbType.SQLITE;
        }
        if(dbType == null)LOG.info("暂不支持 driverName: "+driverName+" 数据库");
        return dbType;
    }

    /**
     * 实际计算
     * @param condition
     * @param dbType
     */
    private void pagingCompute(QueryCondition condition,DbType dbType){
        if(dbType == null)return;
        try {
            Class<?> clazz = Class.forName(dbType.getClassName());
            PagingCompute pc = (PagingCompute) clazz.newInstance();
            pc.compute(condition);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("DbType classname config error");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
}
